home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Gold Medal Software 3
/
Gold Medal Software - Volume 3 (Gold Medal) (1994).iso
/
prog
/
cenvi29.arj
/
CMMEDIT.CMM
< prev
next >
Wrap
Text File
|
1994-03-04
|
16KB
|
409 lines
/**************************************************************************
*** CmmEdit - A simple text editor. This is the tutorial program from ***
*** ver.1 chapter 3 of the CEnvi manual. ***
**************************************************************************/
main(ArgCount,ArgList)
{
FileName = GetFileName(ArgCount,ArgList);
ReadFile(FileName);
if ( Edit() ) // Edit returns TRUE if changes made to file
WriteFile(FileName);
}
GetFileName(argc,argv)
// return a file name from the program input arguments, or prompt user for the
// file name if none was supplied at the command line. exit() program if no
// file name is gotten.
{
// If at least one argument was supplied to main() in addition to the source
// file name (which is always supplied), then that argument is the file name.
if ( 1 < argc )
return(argv[1]);
// File name wasn't supplied on the command line, and so prompt for name.
printf("Enter file name to edit: ");
filespec = gets();
if ( filespec == NULL || filespec[0] == 0 ) // no name was entered so quit
exit(EXIT_FAILURE);
return(filespec);
}
Text[0] = ""; // Text is an array of s text strings; one for each file line
ReadFile(FileSpec) // read FileSpec file into global data. exit() if error.
{
// Open the file, in text mode, for reading into Text.
fp = fopen(FileSpec,"rt");
if ( fp == NULL ) {
// The file doesn't exist, and so ask user if they want to quit. If they
// don't want to create file then simply exit this program. If they do
// want to create file then we're done, as Text is already initialized.
printf("File \"%s\" does not exist. Create file? Y/N ",FileSpec);
do {
key = toupper(getch()); // make uppercase to compare to Y and N
if ( key == 'N' )
exit(EXIT_FAILURE);
} while( key != 'Y' ); // wasn't Y or N, and so try again
} else {
// File opened. Read each line of file into the next element of Text.
for ( LineCount = 0; NULL != (line = fgets(fp)); LineCount++ ) {
// line set to new string for next line in the text file. Set the next
// line of Text to this line.
Text[LineCount] = line;
}
fclose(fp); // Should always close a file that has been opened.
}
}
WriteFile(FileSpec) // write global data to back to FileSpec. exit() if error.
{
// Open FileSpec for writing in text mode. If the file already exists then
// truncate the file. If file doesn't exist then create it.
fp = fopen(FileSpec,"wt");
if ( fp == NULL ) {
printf("\aUnable to open \"%s\" for writing.\a\n");
exit(EXIT_FAILURE);
}
// write every line of Text into fp
for ( i = 0; i <= GetArraySpan(Text); i++ )
fputs( Text[i], fp );
// close fp
fclose(fp);
}
// define movement keys - Give values over 0x100 to distinguish from text
#define MIN_CUR_MOVE 0x101
#define UP 0x101
#define DOWN 0x102
#define LEFT 0x103
#define RIGHT 0x104
#define PG_UP 0x105
#define PG_DN 0x106
#define HOME 0x107
#define END 0x108
#define BK_TAB 0x109
#define DELETE 0x110
GetKeyChar() // return key from keyboard, ascii for above #defined
{
if defined(_DOS_) || defined(_OS2_) {
// DOS and OS/2 return 0 on first getch for extended keys
KeyCode = getch();
if ( KeyCode == 0 ) {
// set value for extended key; these value found using KeyCode.cmd
switch( getch() ) {
case 0x48: KeyCode = UP; break;
case 0x50: KeyCode = DOWN; break;
case 0x4B: KeyCode = LEFT; break;
case 0x4D: KeyCode = RIGHT; break;
case 0x49: KeyCode = PG_UP; break;
case 0x51: KeyCode = PG_DN; break;
case 0x47: KeyCode = HOME; break;
case 0x4F: KeyCode = END; break;
case 0x0F; KeyCode = BK_TAB; break;
case 0x53; KeyCode = DELETE; break;
default: break; // return 0, which will do nothing
}
}
} else {
// Windows version
KeyCode = getch();
if ( 0x100 < KeyCode ) {
switch ( KeyCode ) {
// special values in the following table come from KeyCode.cmm
case 0x126: KeyCode = UP; break;
case 0x128: KeyCode = DOWN; break;
case 0x125: KeyCode = LEFT; break;
case 0x127: KeyCode = RIGHT; break;
case 0x121: KeyCode = PG_UP; break;
case 0x122: KeyCode = PG_DN; break;
case 0x124: KeyCode = HOME; break;
case 0x123: KeyCode = END; break;
case 0x109; KeyCode = BK_TAB; break;
case 0x12E; KeyCode = DELETE; break;
default: KeyCode = 0; break;
}
}
}
return(KeyCode);
}
Edit() // Edit file. This is were the hard work happens. exit() if error.
{ // Return FALSE if no editing was done, else return TRUE.
LineCount = 1 + GetArraySpan(Text); // how many lines in file
// Initialize screen: get its dimensions, and cursor location.
ScreenClear();
ScreenDimension = ScreenSize();
CursorCol = CursorRow = 0; // initialize cursor position
// Starting at row 0, draw all lines on screen. Initialize Start as structure
// for upper-left visible portion of file. Then draw the file.
Start.Row = Start.Col = 0;
DrawVisibleTextLines( Start, ScreenDimension, LineCount );
DrawnStart = Start; // remember which lines were drawn
CursorStatus(CursorRow,CursorCol,Start,ScreenDimension);
// FileWasEdited is boolean to say if changes made
FileWasEdited = FALSE;
// Stay here getting all keyboard input until escape is pressed
#define ESCAPE_KEY '\033'
while ( (key = GetKeyChar()) != ESCAPE_KEY ) {
// special keyboard codes are returned if getch() first
switch( key ) {
case UP: CursorRow--; break;
case DOWN: CursorRow++; break;
case LEFT: CursorCol--; break;
case RIGHT: CursorCol++; break;
case HOME: CursorCol = 0; break;
case END:
// go to end of visible line, but not including newline
CursorCol = strlen(Text[CursorRow]);
if ( 0 < CursorCol && Text[CursorRow][CursorCol-1] == '\n' )
CursorCol--;
break;
case PG_UP:
CursorRow -= (ScreenDimension.row - 1);
Start.Row -= (ScreenDimension.row - 1);
break;
case PG_DN:
CursorRow += (ScreenDimension.row - 1);
Start.Row += (ScreenDimension.row - 1);
break;
#define TABSIZE 8
case '\t':
CursorCol += TABSIZE;
CursorCol -= CursorCol % TABSIZE;
break;
case BK_TAB:
CursorCol -= TABSIZE;
CursorCol -= CursorCol % TABSIZE;
break;
#define BACKSPACE '\010'
case BACKSPACE:
// Back space is just like deleting from one column to the left,
// and so check that we're not on the first column and then move
// left a column and let control fall to DELETE
if ( --CursorCol < 0 ) {
// backspace from beginning of line; move to end of previous line
if ( CursorRow == 0 ) {
// cannot backup to earlier row, so do nothing
CursorCol = 0;
break;
}
CursorCol = strlen(Text[--CursorRow]) - 1;
}
case DELETE:
if ( DeleteCharacterAtCursor(CursorRow,CursorCol,LineCount) ) {
FileWasEdited = TRUE;
DrawnStart.row = -1; // force screen redraw
}
break;
case '\r':
// Add a newline at the current position
InsertAsciiCharacter('\n',Text[CursorRow],CursorCol);
FileWasEdited = TRUE;
// a line must be opened up in Text, and all the data moved
for( i = LineCount++; CursorRow + 1 < i; i-- )
strcpy( Text[i], Text[i-1] );
// move text from after cursor to next line, and end this line
strcpy(Text[CursorRow+1],Text[CursorRow] + CursorCol + 1);
Text[CursorRow][CursorCol + 1] = 0;
// finally, move cursor to beginning of next line, and redraw screen
CursorRow++, CursorCol = 0;
DrawnStart.row = -1; // force screen redraw
break;
default:
if ( isprint(key) ) {
InsertAsciiCharacter(key,Text[CursorRow],CursorCol++);
FileWasEdited = TRUE;
// redraw this row
ScreenCursor(0,CursorRow - Start.row);
printf("%.*s",ScreenDimension.col,Text[CursorRow] + Start.col);
} else {
// the key that was pressed was not handled. Beep at the user as a
// warning, but otherwise alter nothing.
putchar('\a');
}
break;
}
// Check that cursor position has not gone out of range
if ( CursorRow < 0 ) CursorRow = 0;
if ( CursorCol < 0 ) CursorCol = 0;
if ( LineCount <= CursorRow ) CursorRow = LineCount - 1;
// Check that Start.Row has not gone out of range
MaxStartRow = LineCount - (ScreenDimension.row - 1)
if ( MaxStartRow < Start.Row )
Start.Row = MaxStartRow;
if ( Start.Row < 0 ) Start.Row = 0;
// If cursor does not now fit on visible screen, then move
// screen so that cursor does fit on it.
while( CursorRow < Start.Row ) Start.Row--;
while( CursorCol < Start.Col ) Start.Col--;
while( Start.Row + ScreenDimension.Row - 1 <= CursorRow ) Start.Row++;
while( Start.Col + ScreenDimension.Col <= CursorCol ) Start.Col++;
// if screen must be redrawn, then do so now
if ( DrawnStart != Start ) {
ScreenClear();
DrawVisibleTextLines( Start, ScreenDimension, LineCount );
DrawnStart = Start;
}
// key was processed, so redisplay screen state
CursorStatus(CursorRow,CursorCol,Start,ScreenDimension);
}
// Return TRUE if file was edited, else false
ScreenClear();
return(FileWasEdited);
}
InsertAsciiCharacter(c,str,offset) // insert c in str at offset
{
// The newline at the end of the string can be a problem later, so for now
// temporarily remove the newline then we'll put it back in when we're done.
len = strlen(str);
AddNewLine = ( len != 0 && str[len-1] == '\n' );
if ( AddNewLine )
str[--len] = 0;
// If the current cursor position is longer than the line, then add spaces.
while( len < offset )
str[len++] = ' ';
// If this character won't be at end of the string, then move all characters
// from here to the end of the string one space forward. This may be done
// simply with a strcpy because Cmm ensures that overwriting is safe.
if ( offset < len ) {
strcpy(str + offset + 1,str + offset);
len++;
}
// At last, put the character in the string
str[offset] = c;
if ( AddNewLine ) // put the newline character back into the string
strcat(str,"\n");
}
DeleteCharacterAtCursor(row,col,TotalLineCount)
// delete character at cursor position. Return TRUE if a character was
// delete else return FALSE. This function may alter TotalLineCount.
{
str = Text[row];
len = strlen(str);
if ( row < (TotalLineCount - 1) )
len--;
if ( col < len ) {
// This is the simple case. copy string to this location from next char
strcpy(str + col,str + col + 1);
} else {
// deleting from the end of the string or from beyond. Must bring in
// from next row.
if ( row == (TotalLineCount - 1) )
return(FALSE); // no following text to copy to here
// fill in spaces from end of text to this location
for( i = len; i <= col; i++ )
str[i] = ' ';
// copy from next string to the end of this string
strcpy( str + col, Text[row+1] );
// One newline has been removed, and so there are now one fewer lines
// in the file. Copy all of rows down one element in the Text array.
TotalLineCount--;
for ( i = row + 1; i < TotalLineCount; i++ )
Text[i] = Text[i+1];
SetArraySpan(Text,TotalLineCount - 1);
}
return(TRUE);
}
DrawVisibleTextLines(StartPosition,ScreenSize,TextLineCount)
// display visible portion of file. StartPosition is initial .row and .col
// that is visible. ScreenSize show .col and .row width and height of screen.
{
// verify that the screen position is not invalid; negative would be bad.
assert( 0 <= StartPosition.row && 0 <= StartPosition.col );
// Also, this function assumes that at least some lines are visible at the
// top of the screen, and so verify that this is true.
assert( StartPosition.row < TextLineCount );
// draw all visible lines from Text; leave bottom line free for messages.
for ( row = 0; row < (ScreenSize.row-1); row++ ) {
Line = Text[StartPosition.row + row];
// draw this line on the screen from StartPosition.row, remembering
// to clip at the right edge of screen if the line is too long
LineLen = strlen(Line) - StartPosition.col;
if ( 0 < LineLen ) { // only print if characters to print
ScreenCursor(0,row);
printf("%.*s",ScreenSize.col,Line + StartPosition.col);
}
}
}
CursorStatus(CRow,CCol,StartPosition,ScreenSize)
{
// show current file cursor position; based at 1
ScreenCursor(5,ScreenSize.row-1);
printf("Status: row %-3d col %-3d",CRow + 1,CCol + 1);
// put cursor at correct position on screen
ScreenCursor(CCol - StartPosition.Col,CRow - StartPosition.Row);
}
//DebugPrintf(FormatString,arg1,arg2,arg3/*etc...*/)
// // printf() line on bottom of string, then get key
//{
// // format message into a string
// va_start(VaList,FormatString);
// vsprintf(msg,FormatString,VaList);
// va_end(VaList);
//
// // Save the cursor position, display this message on the bottom of the screen,
// // get a key, and then return. This is very non-intrusive.
// SaveCursor = ScreenCursor();
// ClearBottomLine();
// msg[ScreenSize().Col - 1] = '\0'; // don't let line get too long
// while ( NULL != (nl = strchr(msg,'\n')) ) // change newlines to spaces
// nl[0] = '_';
// ScreenCursor(0,ScreenSize().Row-1);
// printf("%s",msg);
// GetKeyChar();
// ClearBottomLine();
// ScreenCursor(SaveCursor.Col,SaveCursor.Row);
//}
//
//ClearBottomLine() // called by DebugPrintf() to clear last lie
//{
// ScreenCursor(0,ScreenSize().Row - 1);
// printf("%*s",ScreenSize().Col-1,"");
//}